package ift605.tp2.sectionB.server;

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import udes.ds.rmi.hw.BasicEquation;
import udes.ds.rmi.hw.Constant;
import udes.ds.rmi.hw.Equation;
import udes.ds.rmi.hw.MultiplicativeEquation;
import udes.ds.rmi.hw.PolynomialEquation;
import udes.ds.rmi.hw.UnknownEquationTypeException;

import ift605.tp2.sectionA.server.MyServer;
import ift605.tp2.sectionA.server.MyServerPublicInterface;
import ift605.tp2.sectionB.client.MyAdminInterface;

@SuppressWarnings("serial")
public class MyAdministrableServer extends MyServer implements MyServerPublicInterface {
	List<MyAdminInterface> listeners = new ArrayList<MyAdminInterface>();
	AdminServer adminS = new AdminServer(); 
	
	MyAdminInterface admin = null;
	
	public class ThreadTuple{
		public Thread thread;
		public Long creationTime;
		
		public ThreadTuple()
		{
			thread = Thread.currentThread();
			creationTime = System.currentTimeMillis();
		}
	}
	Map<Long, ThreadTuple> threadMap = new HashMap<Long, ThreadTuple>(); 

	
	class AdminServer extends UnicastRemoteObject implements MyAdminstrableInterfaceServer
	{
		

		protected AdminServer() throws RemoteException {
			super();
		}

		@Override
		public Map<Long, Long> connect(MyAdminInterface observer) {
			listeners.add(observer);
			Map<Long, Long> returnMap = new HashMap<Long, Long>();
			for(Long processId : threadMap.keySet())
			{
				returnMap.put(processId, System.currentTimeMillis() - threadMap.get(processId).creationTime);
			}
			return returnMap;
		}
		
		@Override
		public void killProcess(Long processIdToKill) {
			if( threadMap.containsKey(processIdToKill))
			{
				threadMap.get(processIdToKill).thread.interrupt();
			}
		}

		@Override
		public void disconnect(MyAdminInterface observer) {
			listeners.remove(observer);
		}
	
	}
	
	
	protected MyAdministrableServer() throws RemoteException {
		super();
	}

	public static void main(String[] args) {
		try {
			MyAdministrableServer server = new MyAdministrableServer();
			Naming.rebind("MyServer", (MyServerPublicInterface)server);
			Naming.rebind("MyServerAdmin", (MyAdminstrableInterfaceServer)server.adminS);
			
			try
			{
				System.out.println("IP Address : " + InetAddress.getLocalHost().getHostAddress());
			}
			catch( Exception e){}
		} 
		catch (RemoteException e) {e.printStackTrace();}
		catch (MalformedURLException e) {e.printStackTrace();}
	}
	
	@Override
	public Equation resolveEquation(Equation aEquation) throws UnknownEquationTypeException {
		Equation answer = null;
		
		threadMap.put(Thread.currentThread().getId(), new ThreadTuple());
		notifyAllAdminClientOnConnection();
		try
		{
			System.out.println("Before : " + aEquation.getUserReadableString());
			Thread.sleep(15000);
			answer = computeDerivative(aEquation);
			System.out.println("After : " + answer.getUserReadableString());
		}
		catch(InterruptedException e)
		{
			System.out.println("Client has been cut");
		}
		notifyAllAdminClientOnDisconnection();
		threadMap.remove(Thread.currentThread().getId());
		return answer;
	}

	private void notifyAllAdminClientOnConnection() {
		for(MyAdminInterface admin : listeners)
		{
			try {
				admin.UpdateOnConnect(Thread.currentThread().getId());
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}
	
	private void notifyAllAdminClientOnDisconnection() {
		for(MyAdminInterface admin : listeners)
		{
			try {
				admin.UpdateOnDisconnect(Thread.currentThread().getId());
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}

}
